home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 726-750 / 741 / rkrm_devices / rkrm_devices.lha / SampleDevice / ramdev.device.asm < prev    next >
Assembly Source File  |  1992-09-03  |  46KB  |  1,416 lines

  1.  
  2. *************************************************************************
  3. *
  4. *   Copyright (C) 1986,1988,1989 Commodore Amiga Inc.  All rights reserved.
  5. *   Permission granted for non-commercial use.
  6. *
  7. *************************************************************************
  8. *
  9. * ramdev.asm -- Skeleton device code.
  10. *
  11. * A sample 4 unit ramdisk that can be bound to an expansion slot device,
  12. * or used without.  Works with the Fast File System.
  13. * This code is required reading for device driver writers.  It contains
  14. * information not found elsewhere.  This code is somewhat old; you probably
  15. * don't want to copy it directly.
  16. *
  17. * This example includes a task, though a task is not actually needed for
  18. * a simple ram disk.  Unlike a single set of hardware registers that
  19. * may need to be shared by multiple tasks, ram can be freely shared.
  20. * This example does not show arbitration of hardware resources.
  21. *
  22. * Tested with CAPE and Metacomco
  23. *
  24. *       Based on mydev.asm
  25. *       10/07/86 Modified by Lee Erickson to be a simple disk device
  26. *            using RAM to simulate a disk.
  27. *       02/02/88 Modified by C. Scheppner, renamed ramdev
  28. *       09/28/88 Repaired by Bryce Nesbitt for new release
  29. *       11/02/88 More clarifications
  30. *       02/01/89 Even more clarifications & warnings
  31. *       02/22/89 START/STOP fix from Marco Papa
  32. *
  33. * Bugs: If RTF_AUTOINIT fails, library base still left in memory.
  34. *
  35. *************************************************************************
  36.  
  37.    SECTION firstsection
  38.  
  39.    NOLIST
  40.    include "exec/types.i"
  41.    include "exec/devices.i"
  42.    include "exec/initializers.i"
  43.    include "exec/memory.i"
  44.    include "exec/resident.i"
  45.    include "exec/io.i"
  46.    include "exec/ables.i"
  47.    include "exec/errors.i"
  48.    include "exec/tasks.i"
  49.    include "hardware/intbits.i"
  50.  
  51.    include "asmsupp.i"  ;standard asmsupp.i, same as used for library
  52.    include "ramdev.i"
  53.  
  54.    IFNE AUTOMOUNT
  55.    include "libraries/expansion.i"
  56.    include "libraries/configvars.i"
  57.    include "libraries/configregs.i"
  58.    ENDC
  59.    LIST
  60.  
  61.  
  62. ABSEXECBASE equ 4   ;Absolute location of the pointer to exec.library base
  63.  
  64.  
  65.    ;------ These don't have to be external, but it helps some
  66.    ;------ debuggers to have them globally visible
  67.    XDEF   Init
  68.    XDEF   Open
  69.    XDEF   Close
  70.    XDEF   Expunge
  71.    XDEF   Null
  72.    XDEF   myName
  73.    XDEF   BeginIO
  74.    XDEF   AbortIO
  75.  
  76.    ;Pull these _LVOs in from amiga.lib
  77.    XLIB   AddIntServer
  78.    XLIB   RemIntServer
  79.    XLIB   Debug
  80.    XLIB   InitStruct
  81.    XLIB   OpenLibrary
  82.    XLIB   CloseLibrary
  83.    XLIB   Alert
  84.    XLIB   FreeMem
  85.    XLIB   Remove
  86.    XLIB   AddPort
  87.    XLIB   AllocMem
  88.    XLIB   AddTask
  89.    XLIB   PutMsg
  90.    XLIB   RemTask
  91.    XLIB   ReplyMsg
  92.    XLIB   Signal
  93.    XLIB   GetMsg
  94.    XLIB   Wait
  95.    XLIB   WaitPort
  96.    XLIB   AllocSignal
  97.    XLIB   SetTaskPri
  98.    XLIB   GetCurrentBinding    ;Use to get list of boards for this driver
  99.    XLIB   MakeDosNode
  100.    XLIB   AddDosNode
  101.    XLIB   CopyMemQuick    ;Highly optimized copy function from exec.library
  102.  
  103.    INT_ABLES        ;Macro from exec/ables.i
  104.  
  105.  
  106. ;-----------------------------------------------------------------------
  107. ; The first executable location.  This should return an error
  108. ; in case someone tried to run you as a program (instead of
  109. ; loading you as a device).
  110.  
  111. FirstAddress:
  112.         moveq    #-1,d0
  113.         rts
  114.  
  115. ;-----------------------------------------------------------------------
  116. ; A romtag structure.  After your driver is brought in from disk, the
  117. ; disk image will be scanned for this structure to discover magic constants
  118. ; about you (such as where to start running you from...).
  119. ;-----------------------------------------------------------------------
  120.  
  121.    ; Most people will not need a priority and should leave it at zero.
  122.    ; the RT_PRI field is used for configuring the roms.  Use "mods" from
  123.    ; wack to look at the other romtags in the system
  124. MYPRI    EQU   0
  125.  
  126. initDDescrip:
  127.                 ;STRUCTURE RT,0
  128.      DC.W    RTC_MATCHWORD    ; UWORD RT_MATCHWORD (Magic cookie)
  129.      DC.L    initDDescrip    ; APTR    RT_MATCHTAG  (Back pointer)
  130.      DC.L    EndCode        ; APTR    RT_ENDSKIP   (To end of this hunk)
  131.      DC.B    RTF_AUTOINIT    ; UBYTE RT_FLAGS     (magic-see "Init:")
  132.      DC.B    VERSION        ; UBYTE RT_VERSION
  133.      DC.B    NT_DEVICE        ; UBYTE RT_TYPE      (must be correct)
  134.      DC.B    MYPRI        ; BYTE    RT_PRI
  135.      DC.L    myName        ; APTR    RT_NAME      (exec name)
  136.      DC.L    idString        ; APTR    RT_IDSTRING  (text string)
  137.      DC.L    Init        ; APTR    RT_INIT
  138.            ; LABEL RT_SIZE
  139.  
  140.  
  141.    ;This name for debugging use
  142.    IFNE INFO_LEVEL  ;If any debugging enabled at all
  143. subSysName:
  144.     dc.b    "ramdev",0
  145.    ENDC
  146.  
  147.    ; this is the name that the device will have
  148. myName:      MYDEVNAME
  149.  
  150.  IFNE  AUTOMOUNT
  151. ExLibName    dc.b 'expansion.library',0   ; Expansion Library Name
  152.  ENDC
  153.  
  154.    ; a major version number.
  155. VERSION:    EQU   37
  156.  
  157.    ; A particular revision.  This should uniquely identify the bits in the
  158.    ; device.  I use a script that advances the revision number each time
  159.    ; I recompile.  That way there is never a question of which device
  160.    ; that really is.
  161. REVISION:   EQU   1
  162.  
  163.    ; this is an identifier tag to help in supporting the device
  164.    ; format is 'name version.revision (d.m.yy)',<cr>,<lf>,<null>
  165. idString:   dc.b   'ramdev 37.1 (28.8.91)',13,10,0
  166.  
  167.    ; force word alignment
  168.    ds.w   0
  169.  
  170.  
  171.    ; The romtag specified that we were "RTF_AUTOINIT".  This means
  172.    ; that the RT_INIT structure member points to one of these
  173.    ; tables below.  If the AUTOINIT bit was not set then RT_INIT
  174.    ; would point to a routine to run.
  175.  
  176. Init:
  177.    DC.L   MyDev_Sizeof        ; data space size
  178.    DC.L   funcTable        ; pointer to function initializers
  179.    DC.L   dataTable        ; pointer to data initializers
  180.    DC.L   initRoutine        ; routine to run
  181.  
  182.  
  183. funcTable:
  184.    ;------ standard system routines
  185.    dc.l   Open
  186.    dc.l   Close
  187.    dc.l   Expunge
  188.    dc.l   Null        ;Reserved for future use!
  189.  
  190.    ;------ my device definitions
  191.    dc.l   BeginIO
  192.    dc.l   AbortIO
  193.  
  194.    ;------ custom extended functions
  195.    dc.l   FunctionA
  196.    dc.l   FunctionB
  197.  
  198.    ;------ function table end marker
  199.    dc.l   -1
  200.  
  201.  
  202.    ;The data table initializes static data structures. The format is
  203.    ;specified in exec/InitStruct routine's manual pages.  The
  204.    ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
  205.    ;The first argument is the offset from the device base for this
  206.    ;byte/word/long. The second argument is the value to put in that cell.
  207.    ;The table is null terminated
  208.    ;
  209. dataTable:
  210.    INITBYTE   LN_TYPE,NT_DEVICE       ;Must be LN_TYPE!
  211.    INITLONG   LN_NAME,myName
  212.    INITBYTE   LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
  213.    INITWORD   LIB_VERSION,VERSION
  214.    INITWORD   LIB_REVISION,REVISION
  215.    INITLONG   LIB_IDSTRING,idString
  216.    DC.W   0   ;terminate list
  217.  
  218.  
  219. ;-------- initRoutine -------------------------------------------------------
  220. ;
  221. ; FOR RTF_AUTOINIT:
  222. ;   This routine gets called after the device has been allocated.
  223. ;   The device pointer is in D0.  The AmigaDOS segment list is in a0.
  224. ;   If it returns the device pointer, then the device will be linked
  225. ;   into the device list.  If it returns NULL, then the device
  226. ;   will be unloaded.
  227. ;
  228. ; IMPORTANT:
  229. ;   If you don't use the "RTF_AUTOINIT" feature, there is an additional
  230. ;   caveat.  If you allocate memory in your Open function, remember that
  231. ;   allocating memory can cause an Expunge... including an expunge of your
  232. ;   device.  This must not be fatal.  The easy solution is don't add your
  233. ;   device to the list until after it is ready for action.
  234. ;
  235. ; This call is single-threaded by exec; please read the description for
  236. ; "Open" below.
  237. ;
  238. ; Register Usage
  239. ; ==============
  240. ; a3 -- Points to temporary RAM
  241. ; a4 -- Expansion library base
  242. ; a5 -- device pointer
  243. ; a6 -- Exec base
  244. ;----------------------------------------------------------------------
  245. initRoutine:
  246.    ;------ get the device pointer into a convenient A register
  247.    PUTMSG   5,<'%s/Init: called'>
  248.    movem.l  d1-d7/a0-a5,-(sp)   ; Preserve ALL modified registers
  249.    move.l   d0,a5
  250.  
  251.    ;------ save a pointer to exec
  252.    move.l   a6,md_SysLib(a5)    ;faster access than move.l 4,a6
  253.  
  254.    ;------ save pointer to our loaded code (the SegList)
  255.    move.l   a0,md_SegList(a5)
  256.  
  257.  IFNE  AUTOMOUNT
  258. **************************************************************************
  259. *
  260. * Here starts the AutoConfig stuff.  If this driver was to be tied to
  261. * an expansion board, you would put this driver in the expansion drawer,
  262. * and be called when BindDrivers finds a board that matches this driver.
  263. * The Commodore-Amiga assigned product number of your board must be
  264. * specified in the "PRODUCT=" field in the TOOLTYPES of this driver's icon.
  265. * GetCurrentBinding() returns your (first) board.
  266. *
  267.    lea.l     ExLibName,A1    ; Get expansion lib. name
  268.    moveq.l   #0,D0
  269.    CALLSYS   OpenLibrary    ; Open the expansion library
  270.    tst.l     D0
  271.    beq         Init_Error
  272.  
  273.    ;------ init_OpSuccess:
  274.    move.l    D0,A4        ;[expansionbase to A4]
  275.    moveq     #0,D3
  276.    lea         md_Base(A5),A0   ; Get the Current Bindings
  277.    moveq     #4,D0          ; Just get address (length = 4 bytes)
  278.    LINKLIB   _LVOGetCurrentBinding,A4
  279.    move.l    md_Base(A5),D0      ; Get start of list
  280.    tst.l     D0        ; If controller not found
  281.    beq         Init_End       ; Exit and unload driver
  282.  
  283.    PUTMSG    10,<'%s/Init: GetCurrentBinding returned non-zero'>
  284.    move.l    D0,A0       ; Get config structure address
  285.    move.l    cd_BoardAddr(A0),md_Base(A5); Save board base address
  286.    bclr.b    #CDB_CONFIGME,cd_Flags(A0); Mark board as configured
  287.  
  288. ;----------------------------------------------------------------------
  289. ;
  290. ; Here we build a packet describing the characteristics of our disk to
  291. ; pass to AmigaDOS.  This serves the same purpose as a "mount" command
  292. ; of this device would.  For disks, it might be useful to actually
  293. ; get this information right from the disk itself.  Just as mount,
  294. ; it could be for multiple partitions on the single physical device.
  295. ; For this example, we will simply hard code the appropriate parameters.
  296. ;
  297. ; The AddDosNode call adds things to dos's list without needing to
  298. ; use mount.  We'll mount all 4 of our units whenever we are
  299. ; started.
  300. ;
  301. ;-----------------------------------------------------------------------
  302.  
  303. ;!!! If your card was successfully configured, you can mount the
  304. ;!!! units as DOS nodes
  305.  
  306.    ;------   Allocate temporary RAM to build MakeDosNode parameter packet
  307.    move.l    #MEMF_CLEAR!MEMF_PUBLIC,d1
  308.    move.l    #mdn_Sizeof,d0   ; Enough room for our parameter packet
  309.    CALLSYS   AllocMem
  310.    move.l    d0,a3        ;:BUG: AllocMem error not checked here.
  311.  
  312.    ;-----   Use InitStruct to initialize the constant portion of packet
  313.    move.l    d0,a2       ; Point to memory to initialize
  314.    moveq.l   #0,d0       ; Don't need to re-zero it
  315.    lea.l     mdn_Init(pc),A1
  316.    CALLSYS   InitStruct
  317.  
  318.    lea         mdn_dName(a3),a0     ; Get addr of Device name
  319.    move.l    a0,mdn_dosName(a3)   ;   and save in environment
  320.  
  321.    moveq     #0,d6          ; Now tell AmigaDOS about all units UNITNUM
  322. Uloop:
  323.    move.b    d6,d0         ; Get unit number
  324.    add.b     #$30,d0         ; Make ASCII, minus 1
  325.    move.b    d0,mdn_dName+2(a3)   ;   and store in name
  326.    move.l    d6,mdn_unit(a3)      ; Store unit # in environment
  327.  
  328. ;
  329. ;! Before adding to the dos list, you should really check if you
  330. ;! are about to cause a name collision.  This example does not.
  331. ;
  332.  
  333.    move.l    a3,a0
  334.    LINKLIB   _LVOMakeDosNode,a4   ; Build AmigaDOS structures
  335.    ;This can fail, but so what?
  336.    move.l    d0,a0          ; Get deviceNode address
  337.    moveq.l   #0,d0          ; Set device priority to 0
  338.    moveq.l   #0,d1
  339. *  moveq.l   #ADNF_STARTPROC,d1     ; See note below
  340.    ;It's ok to pass a zero in here
  341.    LINKLIB   _LVOAddDosNode,a4
  342.  
  343.  
  344. ; ADNF_STARTPROC will work, but only if dn_SegList is filled in
  345. ; in the SegPtr of the handler task.
  346.  
  347.  
  348.    addq     #1,d6      ; Bump unit number
  349.    cmp.b    #MD_NUMUNITS,d6
  350.    bls.s    Uloop      ; Loop until all units installed
  351.  
  352.    move.l   a3,a1      ; Return RAM to system
  353.    move.l   #mdn_Sizeof,d0
  354.    CALLSYS  FreeMem
  355.  
  356. Init_End:
  357.  
  358.    move.l   a4,a1      ; Now close expansion library
  359.    CALLSYS  CloseLibrary
  360. *
  361. *   You would normally set d0 to a NULL if your initialization failed,
  362. *   but I'm not doing that for this demo, since it is unlikely
  363. *   you actually have a board with any particular manufacturer ID
  364. *   installed when running this demo.
  365. *************************************************************************
  366.   ENDC
  367.  
  368.    move.l   a5,d0
  369. Init_Error:
  370.    movem.l  (sp)+,d1-d7/a0-a5
  371.    rts
  372.  
  373.  
  374. ;----------------------------------------------------------------------
  375. ;
  376. ; Here begins the system interface commands.  When the user calls
  377. ; OpenDevice/CloseDevice/RemDevice, this eventually gets translated
  378. ; into a call to the following routines (Open/Close/Expunge).
  379. ; Exec has already put our device pointer in a6 for us.
  380. ;
  381. ; IMPORTANT:
  382. ;   These calls are guaranteed to be single-threaded; only one task
  383. ;   will execute your Open/Close/Expunge at a time.
  384. ;
  385. ;   For Kickstart V33/34, the single-threading method involves "Forbid".
  386. ;   There is a good chance this will change.  Anything inside your
  387. ;   Open/Close/Expunge that causes a direct or indirect Wait() will break
  388. ;   the Forbid().  If the Forbid() is broken, some other task might
  389. ;   manage to enter your Open/Close/Expunge code at the same time.
  390. ;   Take care!
  391. ;
  392. ; Since exec has turned off task switching while in these routines
  393. ; (via Forbid/Permit), we should not take too long in them.
  394. ;
  395. ;----------------------------------------------------------------------
  396.  
  397.    ; Open sets the IO_ERROR field on an error.    If it was successfull,
  398.    ; we should also set up the IO_UNIT and LN_TYPE fields.
  399.    ; exec takes care of setting up IO_DEVICE.
  400.  
  401. Open:       ; ( device:a6, iob:a1, unitnum:d0, flags:d1 )
  402.  
  403. ;** Subtle point: any AllocMem() call can cause a call to this device's
  404. ;** expunge vector.  If LIB_OPENCNT is zero, the device might get expunged.
  405.    addq.w   #1,LIB_OPENCNT(a6)  ;Fake an opener for duration of call <|>
  406.  
  407.    PUTMSG   20,<'%s/Open: called'>
  408.    movem.l  d2/a2/a3/a4,-(sp)
  409.  
  410.    move.l   a1,a2      ; save the iob
  411.  
  412.    ;------ see if the unit number is in range    *!* UNIT 0 to 3 *!*
  413.    cmp.l   #MD_NUMUNITS,d0
  414.    bcc.s   Open_Range_Error   ; unit number out of range (BHS)
  415.  
  416.    ;------ see if the unit is already initialized
  417.    move.l   d0,d2      ; save unit number
  418.    lsl.l    #2,d0
  419.    lea.l    md_Units(a6,d0.l),a4
  420.    move.l   (a4),d0
  421.    bne.s    Open_UnitOK
  422.  
  423.    ;------ try and conjure up a unit
  424.    bsr        InitUnit    ;scratch:a3 unitnum:d2 devpoint:a6
  425.  
  426.    ;------ see if it initialized OK
  427.    move.l   (a4),d0
  428.    beq.s    Open_Error
  429.  
  430. Open_UnitOK:
  431.    move.l   d0,a3      ; unit pointer in a3
  432.    move.l   d0,IO_UNIT(a2)
  433.  
  434.    ;------ mark us as having another opener
  435.    addq.w   #1,LIB_OPENCNT(a6)
  436.    addq.w   #1,UNIT_OPENCNT(a3)     ;Internal bookkeeping
  437.  
  438.    ;------ prevent delayed expunges
  439.    bclr     #LIBB_DELEXP,md_Flags(a6)
  440.  
  441.    CLEAR    d0
  442.    move.b   d0,IO_ERROR(a2)
  443.    move.b   #NT_REPLYMSG,LN_TYPE(a2) ;IMPORTANT: Mark IORequest as "complete"
  444.  
  445. Open_End:
  446.  
  447.    subq.w   #1,LIB_OPENCNT(a6) ;** End of expunge protection <|>
  448.    movem.l  (sp)+,d2/a2/a3/a4
  449.    rts
  450.  
  451. Open_Range_Error:
  452. Open_Error:
  453.    moveq    #IOERR_OPENFAIL,d0
  454.    move.b   d0,IO_ERROR(a2)
  455.    move.l   d0,IO_DEVICE(a2)    ;IMPORTANT: trash IO_DEVICE on open failure
  456.    PUTMSG   2,<'%s/Open: failed'>
  457.    bra.s    Open_End
  458.  
  459.  
  460. ;----------------------------------------------------------------------------
  461. ; There are two different things that might be returned from the Close
  462. ; routine.  If the device wishes to be unloaded, then Close must return
  463. ; the segment list (as given to Init).  Otherwise close MUST return NULL.
  464.  
  465. Close:        ; ( device:a6, iob:a1 )
  466.    movem.l  d1/a2-a3,-(sp)
  467.    PUTMSG   20,<'%s/Close: called'>
  468.  
  469.    move.l   a1,a2
  470.  
  471.    move.l   IO_UNIT(a2),a3
  472.  
  473.    ;------ IMPORTANT: make sure the IORequest is not used again
  474.    ;------ with a -1 in IO_DEVICE, any BeginIO() attempt will
  475.    ;------ immediatly halt (which is better than a subtle corruption
  476.    ;------ that will lead to hard-to-trace crashes!!!!!!!!!!!!!!!!!!
  477.    moveq.l  #-1,d0
  478.    move.l   d0,IO_UNIT(a2)      ;We're closed...
  479.    move.l   d0,IO_DEVICE(a2)    ;customers not welcome at this IORequest!!
  480.  
  481.    ;------ see if the unit is still in use
  482.    subq.w   #1,UNIT_OPENCNT(a3)
  483.  
  484. ;!!!!!! Since this example is a RAM disk (and we don't want the contents to
  485. ;!!!!!! disappear between opens, ExpungeUnit will be skipped here.  It would
  486. ;!!!!!! be used for drivers of "real" devices
  487. ;!!!!!!   bne.s   Close_Device
  488. ;!!!!!!   bsr      ExpungeUnit
  489.  
  490. Close_Device:
  491.    CLEAR   d0
  492.    ;------ mark us as having one fewer openers
  493.    subq.w  #1,LIB_OPENCNT(a6)
  494.  
  495.    ;------ see if there is anyone left with us open
  496.    bne.s   Close_End
  497.  
  498.    ;------ see if we have a delayed expunge pending
  499.    btst    #LIBB_DELEXP,md_Flags(a6)
  500.    beq.s   Close_End
  501.  
  502.    ;------ do the expunge
  503.    bsr       Expunge
  504.  
  505. Close_End:
  506.    movem.l   (sp)+,d1/a2-a3
  507.    rts                ;MUST return either zero or the SegList!!!
  508.  
  509.  
  510. ;------- Expunge -----------------------------------------------------------
  511. ;
  512. ; Expunge is called by the memory allocator when the system is low on
  513. ; memory.
  514. ;
  515. ; There are two different things that might be returned from the Expunge
  516. ; routine.  If the device is no longer open then Expunge may return the
  517. ; segment list (as given to Init).  Otherwise Expunge may set the
  518. ; delayed expunge flag and return NULL.
  519. ;
  520. ; One other important note: because Expunge is called from the memory
  521. ; allocator, it may NEVER Wait() or otherwise take long time to complete.
  522. ;
  523. ;    A6        - library base (scratch)
  524. ;    D0-D1/A0-A1 - scratch
  525. ;
  526. Expunge:   ; ( device: a6 )
  527.    PUTMSG   10,<'%s/Expunge: called'>
  528.  
  529.    movem.l  d1/d2/a5/a6,-(sp)   ; Save ALL modified registers
  530.    move.l   a6,a5
  531.    move.l   md_SysLib(a5),a6
  532.  
  533.    ;------ see if anyone has us open
  534.    tst.w   LIB_OPENCNT(a5)
  535. ;!!!!!    The following line is commented out for this RAM disk demo, since
  536. ;!!!!!    we don't want the RAM to be freed after FORMAT, for example.
  537. ;   beq    1$
  538.  
  539.    ;------ it is still open.  set the delayed expunge flag
  540.    bset    #LIBB_DELEXP,md_Flags(a5)
  541.    CLEAR   d0
  542.    bra.s   Expunge_End
  543.  
  544. 1$:
  545.    ;------ go ahead and get rid of us.    Store our seglist in d2
  546.    move.l   md_SegList(a5),d2
  547.  
  548.    ;------ unlink from device list
  549.    move.l    a5,a1
  550.    CALLSYS   Remove        ;Remove first (before FreeMem)
  551.  
  552.    ;
  553.    ; device specific closings here...
  554.    ;
  555.  
  556.    ;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE)
  557.    move.l   a5,a1        ;Devicebase
  558.    CLEAR    d0
  559.    move.w   LIB_NEGSIZE(a5),d0
  560.    suba.l   d0,a1        ;Calculate base of functions
  561.    add.w    LIB_POSSIZE(a5),d0  ;Calculate size of functions + data area
  562.    CALLSYS  FreeMem
  563.  
  564.    ;------ set up our return value
  565.    move.l   d2,d0
  566.  
  567. Expunge_End:
  568.    movem.l  (sp)+,d1/d2/a5/a6
  569.    rts
  570.  
  571.  
  572. ;------- Null ---------------------------------------------------------------
  573. Null:
  574.    PUTMSG  1,<'%s/Null: called'>
  575.    CLEAR   d0
  576.    rts        ;The "Null" function MUST return NULL.
  577.  
  578.  
  579. ;------- Custom ------------------------------------------------------------
  580. ;
  581. ;Two "do nothing" device-specific functions
  582. ;
  583. FunctionA:
  584.     add.l   d1,d0   ;Add
  585.     rts
  586. FunctionB:
  587.     add.l   d0,d0   ;Double
  588.     rts
  589.  
  590.  
  591. ****************************************************************************
  592.  
  593. InitUnit:   ; ( d2:unit number, a3:scratch, a6:devptr )
  594.    PUTMSG   30,<'%s/InitUnit: called'>
  595.    movem.l  d2-d4/a2,-(sp)
  596.  
  597.    ;------ allocate unit memory
  598.    move.l   #MyDevUnit_Sizeof,d0
  599.    move.l   #MEMF_PUBLIC!MEMF_CLEAR,d1
  600.    LINKSYS  AllocMem,md_SysLib(a6)
  601.    tst.l    d0
  602.    beq        InitUnit_End
  603.    move.l   d0,a3
  604.  
  605.    moveq.l  #0,d0       ; Don't need to re-zero it
  606.    move.l   a3,a2       ; InitStruct is initializing the UNIT
  607.    lea.l    mdu_Init(pc),A1
  608.    LINKSYS  InitStruct,md_SysLib(a6)
  609.  
  610.    ;!! IMPORTANT !!
  611.    move.l   #42414400,mdu_RAM(a3)   ;Mark offset zero as ASCII "BAD "
  612.    ;!! IMPORTANT !!
  613.  
  614.    move.b   d2,mdu_UnitNum(a3)      ;initialize unit number
  615.    move.l   a6,mdu_Device(a3)       ;initialize device pointer
  616.  
  617.    ;------ start up the unit task.  We do a trick here --
  618.    ;------ we set his message port to PA_IGNORE until the
  619.    ;------ new task has a change to set it up.
  620.    ;------ We cannot go to sleep here: it would be very nasty
  621.    ;------ if someone else tried to open the unit
  622.    ;------ (exec's OpenDevice has done a Forbid() for us --
  623.    ;------ we depend on this to become single threaded).
  624.  
  625.    ;------ Initialize the stack information
  626.    lea        mdu_stack(a3),a0          ; Low end of stack
  627.    move.l   a0,mdu_tcb+TC_SPLOWER(a3)
  628.    lea        MYPROCSTACKSIZE(a0),a0    ; High end of stack
  629.    move.l   a0,mdu_tcb+TC_SPUPPER(a3)
  630.    move.l   a3,-(A0)                  ; argument -- unit ptr (send on stack)
  631.    move.l   a0,mdu_tcb+TC_SPREG(a3)
  632.    lea        mdu_tcb(a3),a0
  633.    move.l   a0,MP_SIGTASK(a3)
  634.  
  635.    IFGE INFO_LEVEL-30
  636.        move.l    a0,-(SP)
  637.        move.l    a3,-(SP)
  638.        PUTMSG    30,<'%s/InitUnit, unit= %lx, task=%lx'>
  639.        addq.l    #8,sp
  640.    ENDC
  641.  
  642.    ;------ initialize the unit's message port's list
  643.    lea        MP_MSGLIST(a3),a0
  644.    NEWLIST  a0            ;<- IMPORTANT! Lists MUST! have NEWLIST
  645.                 ;work magic on them before use.  (AddPort()
  646.                 ;can do this for you)
  647.  
  648.    IFD     INTRRUPT
  649.    move.l   a3,mdu_is+IS_DATA(a3)   ; Pass unit addr to interrupt server
  650.    ENDC
  651.  
  652. ;   Startup the task
  653.    lea        mdu_tcb(a3),a1
  654.    lea        Task_Begin(PC),a2
  655.    move.l   a3,-(sp)      ; Preserve UNIT pointer
  656.    lea        -1,a3      ; generate address error
  657.               ; if task ever "returns" (we RemTask() it
  658.               ; to get rid of it...)
  659.    CLEAR   d0
  660.    PUTMSG   30,<'%s/About to add task'>
  661.    LINKSYS AddTask,md_SysLib(a6)
  662.    move.l   (sp)+,a3      ; restore UNIT pointer
  663.  
  664.    ;------ mark us as ready to go
  665.    move.l   d2,d0      ; unit number
  666.    lsl.l    #2,d0
  667.    move.l   a3,md_Units(a6,d0.l)   ; set unit table
  668.    PUTMSG   30,<'%s/InitUnit: ok'>
  669.  
  670. InitUnit_End:
  671.    movem.l   (sp)+,d2-d4/a2
  672.    rts
  673.  
  674.  
  675. ;---------------------------------------------------------------------------
  676. FreeUnit:   ; ( a3:unitptr, a6:deviceptr )
  677.    move.l   a3,a1
  678.    move.l   #MyDevUnit_Sizeof,d0
  679.    LINKSYS  FreeMem,md_SysLib(a6)
  680.    rts
  681.  
  682. ;---------------------------------------------------------------------------
  683. ExpungeUnit:   ; ( a3:unitptr, a6:deviceptr )
  684.    PUTMSG   10,<'%s/ExpungeUnit: called'>
  685.    move.l   d2,-(sp)
  686.  
  687. ;
  688. ; If you can expunge you unit, and each unit has it's own interrupts,
  689. ; you must remember to remove its interrupt server
  690. ;
  691.  
  692.    IFD     INTRRUPT
  693.    lea.l   mdu_is(a3),a1              ; Point to interrupt structure
  694.    moveq   #INTB_PORTS,d0          ; Portia interrupt bit 3
  695.    LINKSYS RemIntServer,md_SysLib(a6) ;Now remove the interrupt server
  696.    ENDC
  697.  
  698.    ;------ get rid of the unit's task.  We know this is safe
  699.    ;------ because the unit has an open count of zero, so it
  700.    ;------ is 'guaranteed' not in use.
  701.    lea     mdu_tcb(a3),a1
  702.    LINKSYS RemTask,md_SysLib(a6)
  703.  
  704.    ;------ save the unit number
  705.    CLEAR   d2
  706.    move.b  mdu_UnitNum(a3),d2
  707.  
  708.    ;------ free the unit structure.
  709.    bsr       FreeUnit
  710.  
  711.    ;------ clear out the unit vector in the device
  712.    lsl.l   #2,d2
  713.    clr.l   md_Units(a6,d2.l)
  714.  
  715.    move.l  (sp)+,d2
  716.    rts
  717.  
  718.  
  719.  
  720. *****************************************************************************
  721. ;
  722. ; here begins the device functions
  723. ;
  724. ;----------------------------------------------------------------------------
  725. ; cmdtable is used to look up the address of a routine that will
  726. ; implement the device command.
  727. ;
  728. ; NOTE: the "extended" commands (ETD_READ/ETD_WRITE) have bit 15 set!
  729. ; We deliberately refuse to operate on such commands.  However a driver
  730. ; that supports removable media may want to implement this.  One
  731. ; open issue is the handling of the "seclabel" area. It is probably
  732. ; best to reject any command with a non-null "seclabel" pointer.
  733. ;
  734. cmdtable:
  735.    DC.L   Invalid    ;$00000001  ;0    CMD_INVALID
  736.    DC.L   MyReset    ;$00000002  ;1    CMD_RESET
  737.    DC.L   RdWrt     ;$00000004  ;2    CMD_READ    (\/common)
  738.    DC.L   RdWrt     ;$00000008  ;3    CMD_WRITE    (/\common)  ETD_
  739.    DC.L   Update    ;$00000010  ;4    CMD_UPDATE    (NO-OP)     ETD_
  740.    DC.L   Clear     ;$00000020  ;5    CMD_CLEAR    (NO-OP)     ETD_
  741.    DC.L   MyStop    ;$00000040  ;6    CMD_STOP            ETD_
  742.    DC.L   Start     ;$00000080  ;7    CMD_START
  743.    DC.L   Flush     ;$00000100  ;8    CMD_FLUSH
  744.    DC.L   Motor     ;$00000200  ;9    TD_MOTOR    (NO-OP)     ETD_
  745.    DC.L   Seek        ;$00000400  ;A    TD_SEEK     (NO-OP)     ETD_
  746.    DC.L   RdWrt     ;$00000800  ;B    TD_FORMAT    (Same as write)
  747.    DC.L   MyRemove    ;$00001000  ;C    TD_REMOVE    (NO-OP)
  748.    DC.L   ChangeNum    ;$00002000  ;D    TD_CHANGENUM    (returns 0)
  749.    DC.L   ChangeState    ;$00004000  ;E    TD_CHANGESTATE    (returns 0)
  750.    DC.L   ProtStatus    ;$00008000  ;F    TD_PROTSTATUS    (returns 0)
  751.    DC.L   RawRead    ;$00010000  ;10 TD_RAWREAD    (INVALID)
  752.    DC.L   RawWrite    ;$00020000  ;11 TD_RAWWRITE    (INVALID)
  753.    DC.L   GetDriveType    ;$00040000  ;12 TD_GETDRIVETYPE (Returns 1)
  754.    DC.L   GetNumTracks    ;$00080000  ;13 TD_GETNUMTRACKS (Returns NUMTRKS)
  755.    DC.L   AddChangeInt    ;$00100000  ;14 TD_ADDCHANGEINT (NO-OP)
  756.    DC.L   RemChangeInt    ;$00200000  ;15 TD_REMCHANGEINT (NO-OP)
  757. cmdtable_end:
  758.  
  759. ; this define is used to tell which commands should be handled
  760. ; immediately (on the caller's schedule).
  761. ;
  762. ; The immediate commands are Invalid, Reset, Stop, Start, Flush
  763. ;
  764. ; Note that this method limits you to just 32 device specific commands,
  765. ; which may not be enough.
  766. ;IMMEDIATES   EQU   %00000000000000000000000111000011
  767. ;;             --------========--------========
  768. ;;             FEDCBA9876543210FEDCBA9876543210
  769.  
  770. ;;An alternate version.  All commands that are trivially short
  771. ;;and %100 reentrant are included.  This way you won't get the
  772. ;;task switch overhead for these commands.
  773. ;;
  774. IMMEDIATES   EQU   %11111111111111111111011111110011
  775. ;            --------========--------========
  776. ;            FEDCBA9876543210FEDCBA9876543210
  777.  
  778.     IFD   INTRRUPT   ; if using interrupts,
  779. ; These commands can NEVER be done "immediately" if using interrupts,
  780. ; since they would "wait" for the interrupt forever!
  781. ; Read, Write, Format
  782. NEVERIMMED   EQU   $0000080C
  783.     ENDC
  784.  
  785.  
  786. ;--------------------------------
  787. ; BeginIO starts all incoming io.  The IO is either queued up for the
  788. ; unit task or processed immediately.
  789. ;
  790. ;
  791. ; BeginIO often is given the responsibility of making devices single
  792. ; threaded... so two tasks sending commands at the same time don't cause
  793. ; a problem.  Once this has been done, the command is dispatched via
  794. ; PerformIO.
  795. ;
  796. ; There are many ways to do the threading.  This example uses the
  797. ; UNITB_ACTIVE bit.  Be sure this is good enough for your device before
  798. ; using!  Any method is ok.  If immediate access can not be obtained, the
  799. ; request is queued for later processing.
  800. ;
  801. ; Some IO requests do not need single threading, these can be performed
  802. ; immediatley.
  803. ;
  804. ; IMPORTANT:
  805. ;   The exec WaitIO() function uses the IORequest node type (LN_TYPE)
  806. ;   as a flag.    If set to NT_MESSAGE, it assumes the request is
  807. ;   still pending and will wait.  If set to NT_REPLYMSG, it assumes the
  808. ;   request is finished.  It's the responsibility of the device driver
  809. ;   to set the node type to NT_MESSAGE before returning to the user.
  810. ;
  811. BeginIO:   ; ( iob: a1, device:a6 )
  812.  
  813.     IFGE INFO_LEVEL-1
  814.     bchg.b    #1,$bfe001  ;Blink the power LED
  815.     ENDC
  816.     IFGE INFO_LEVEL-3
  817.      clr.l    -(sp)
  818.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  819.      PUTMSG   3,<'%s/BeginIO  -- $%lx'>
  820.      addq.l   #4,sp
  821.     ENDC
  822.  
  823.     movem.l   d1/a0/a3,-(sp)
  824.  
  825.     move.b  #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work
  826.     move.l  IO_UNIT(a1),a3          ;bookkeeping -> what unit to play with
  827.     move.w  IO_COMMAND(a1),d0
  828.  
  829.     ;Do a range check & make sure ETD_XXX type requests are rejected
  830.     cmp.w   #MYDEV_END,d0    ;Compare all 16 bits
  831.     bcc     BeginIO_NoCmd    ;no, reject it.  (bcc=bhs - unsigned)
  832.  
  833.     ;------ process all immediate commands no matter what
  834.     move.l  #IMMEDIATES,d1
  835.     DISABLE a0            ;<-- Ick, nasty stuff, but needed here.
  836.     btst.l  d0,d1
  837.     bne     BeginIO_Immediate
  838.  
  839.     IFD   INTRRUPT   ; if using interrupts,
  840.      ;------ queue all NEVERIMMED commands no matter what
  841.      move.w  #NEVERIMMED,d1
  842.      btst    d0,d1
  843.      bne.s   BeginIO_QueueMsg
  844.     ENDC
  845.  
  846.  
  847.     ;------ see if the unit is STOPPED.  If so, queue the msg.
  848.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  849.     bne     BeginIO_QueueMsg
  850.  
  851.  
  852.     ;------ This is not an immediate command.  See if the device is
  853.     ;------ busy.  If the device is not, do the command on the
  854.     ;------ user schedule.  Else fire up the task.
  855.     ;------ This type of arbitration is not really needed for a ram
  856.     ;------ disk, but is essential for a device to reliably work
  857.     ;------ with shared hardware
  858.     ;------
  859.     ;------ When the lines below are ";" commented out, the task gets
  860.     ;------ a better workout.  When the lines are active, the calling
  861.     ;------ process is usually used for the operation.
  862.     ;------
  863.     ;------ REMEMBER:::: Never Wait() on the user's schedule in BeginIO()!
  864.     ;------ The only exception is when the user has indicated it is ok
  865.     ;------ by setting the "quick" bit.  Since this device copies from
  866.     ;------ ram that never needs to be waited for, this subtlely may not
  867.     ;------ be clear.
  868.     ;------
  869.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)   ;<---- comment out these
  870.     beq.s   BeginIO_Immediate           ;<---- lines to test task.
  871.  
  872.  
  873.     ;------ we need to queue the device.  mark us as needing
  874.     ;------ task attention.  Clear the quick flag
  875. BeginIO_QueueMsg:
  876.     bset    #UNITB_INTASK,UNIT_FLAGS(a3)
  877.     bclr    #IOB_QUICK,IO_FLAGS(a1)   ;We did NOT complete this quickly
  878.     ENABLE  a0
  879.  
  880.  
  881.     IFGE INFO_LEVEL-250
  882.      move.l  a1,-(sp)
  883.      move.l  a3,-(sp)
  884.      PUTMSG  250,<'%s/PutMsg: Port=%lx Message=%lx'>
  885.      addq.l  #8,sp
  886.     ENDC
  887.  
  888.     move.l  a3,a0
  889.     LINKSYS  PutMsg,md_SysLib(a6)   ;Port=a0, Message=a1
  890.     bra.s   BeginIO_End
  891.     ;----- return to caller before completing
  892.  
  893.  
  894.     ;------ Do it on the schedule of the calling process
  895.     ;------
  896. BeginIO_Immediate:
  897.     ENABLE  a0
  898.     bsr.s   PerformIO
  899.  
  900. BeginIO_End:
  901.     PUTMSG  200,<'%s/BeginIO_End'>
  902.     movem.l (sp)+,d1/a0/a3
  903.     rts
  904.  
  905. BeginIO_NoCmd:
  906.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  907.     bra.s   BeginIO_End
  908.  
  909.  
  910. ;
  911. ; PerformIO actually dispatches an io request.    It might be called from
  912. ; the task, or directly from BeginIO (thus on the callers's schedule)
  913. ;
  914. ; It expects a3 to already
  915. ; have the unit pointer in it.    a6 has the device pointer (as always).
  916. ; a1 has the io request.  Bounds checking has already been done on
  917. ; the I/O Request.
  918. ;
  919.  
  920. PerformIO:   ; ( iob:a1, unitptr:a3, devptr:a6 )
  921.     IFGE INFO_LEVEL-150
  922.      clr.l    -(sp)
  923.      move.w   IO_COMMAND(a1),2(sp)  ;Get entire word
  924.      PUTMSG   150,<'%s/PerformIO -- $%lx'>
  925.      addq.l   #4,sp
  926.     ENDC
  927.  
  928.     moveq   #0,d0
  929.     move.b  d0,IO_ERROR(A1)     ; No error so far
  930.     move.b  IO_COMMAND+1(a1),d0 ;Look only at low byte
  931.     lsl.w   #2,d0        ; Multiply by 4 to get table offset
  932.     lea.l   cmdtable(pc),a0
  933.     move.l  0(a0,d0.w),a0
  934.  
  935.     jmp     (a0)    ;iob:a1  unit:a3  devprt:a6
  936.  
  937.  
  938.  
  939. ;
  940. ; TermIO sends the IO request back to the user.  It knows not to mark
  941. ; the device as inactive if this was an immediate request or if the
  942. ; request was started from the server task.
  943. ;
  944.  
  945. TermIO:      ; ( iob:a1, unitptr:a3, devptr:a6 )
  946.     PUTMSG  160,<'%s/TermIO'>
  947.     move.w  IO_COMMAND(a1),d0
  948.  
  949.     move.w  #IMMEDIATES,d1
  950.     btst    d0,d1
  951.     bne.s   TermIO_Immediate    ;IO was immediate, don't do task stuff...
  952.  
  953.     ;------ we may need to turn the active bit off.
  954.     btst    #UNITB_INTASK,UNIT_FLAGS(a3)
  955.     bne.s   TermIO_Immediate    ;IO was came from task, don't clear ACTIVE...
  956.  
  957.     ;------ the task does not have more work to do
  958.     bclr    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  959.  
  960. TermIO_Immediate:
  961.     ;------ if the quick bit is still set then we don't need to reply
  962.     ;------ msg -- just return to the user.
  963.     btst    #IOB_QUICK,IO_FLAGS(a1)
  964.     bne.s   TermIO_End
  965.     LINKSYS ReplyMsg,md_SysLib(a6)      ;a1-message
  966.     ;(ReplyMsg sets the LN_TYPE to NT_REPLYMSG)
  967.  
  968. TermIO_End:
  969.     rts
  970.  
  971.  
  972. *****************************************************************************
  973. ;
  974. ; Here begins the functions that implement the device commands
  975. ; all functions are called with:
  976. ;   a1 -- a pointer to the io request block
  977. ;   a3 -- a pointer to the unit
  978. ;   a6 -- a pointer to the device
  979. ;
  980. ; Commands that conflict with 68000 instructions have a "My" prepended
  981. ; to them.
  982. ;----------------------------------------------------------------------
  983.  
  984. ;We can't AbortIO anything, so don't touch the IORequest!
  985. ;
  986. ;AbortIO() is a REQUEST to "hurry up" processing of an IORequest.
  987. ;If the IORequest was already complete, nothing happens (if an IORequest
  988. ;is quick or LN_TYPE=NT_REPLYMSG, the IORequest is complete).
  989. ;The message must be replied with ReplyMsg(), as normal.
  990. ;
  991. AbortIO:    ; ( iob: a1, device:a6 )
  992.     moveq   #IOERR_NOCMD,d0 ;return "AbortIO() request failed"
  993.     rts
  994.  
  995. RawRead:    ; 10 Not supported   (INVALID)
  996. RawWrite:    ; 11 Not supported   (INVALID)
  997. Invalid:
  998.     move.b  #IOERR_NOCMD,IO_ERROR(a1)
  999.     bra.s   TermIO
  1000.  
  1001. ;
  1002. ; Update and Clear are internal buffering commands.  Update forces all
  1003. ; io out to its final resting spot, and does not return until this is
  1004. ; totally done.  Since this is automatic in a ramdisk, we simply return "Ok".
  1005. ;
  1006. ; Clear invalidates all internal buffers.  Since this device
  1007. ; has no internal buffers, these commands do not apply.
  1008. ;
  1009. Update:
  1010. Clear:
  1011. MyReset:            ;Do nothing (nothing reasonable to do)
  1012. AddChangeInt:            ;Do nothing
  1013. RemChangeInt:            ;Do nothing
  1014. MyRemove:            ;Do nothing
  1015. Seek:                ;Do nothing
  1016. Motor:                ;Do nothing
  1017. ChangeNum:            ;Return zero (changecount =0)
  1018. ChangeState:            ;Zero indicates disk inserted
  1019. ProtStatus:            ;Zero indicates unprotected
  1020.     clr.l   IO_ACTUAL(a1)
  1021.     bra.s   TermIO
  1022.  
  1023.  
  1024. GetDriveType:            ;make it look like 3.5" (90mm) drive
  1025.     moveq   #DRIVE3_5,d0
  1026.     move.l  d0,IO_ACTUAL(a1)
  1027.     bra.s   TermIO
  1028.  
  1029.  
  1030. GetNumTracks:
  1031.     move.l  #RAMSIZE/BYTESPERTRACK,IO_ACTUAL(a1) ;Number of tracks
  1032.     bra.s   TermIO
  1033.  
  1034. ;
  1035. ; Foo and Bar are two device specific commands that are provided just
  1036. ; to show you how commands are added.  They currently return that
  1037. ; no work was done.
  1038. ;
  1039. Foo:
  1040. Bar:
  1041.     clr.l   IO_ACTUAL(a1)
  1042.     bra     TermIO
  1043.  
  1044.  
  1045. ;---------------------------------------------------------------------------
  1046. ; This device is designed so that no combination of bad
  1047. ; inputs can ever cause the device driver to crash.
  1048. ;---------------------------------------------------------------------------
  1049. RdWrt:
  1050.     IFGE INFO_LEVEL-200
  1051.     move.l    IO_DATA(a1),-(sp)
  1052.     move.l    IO_OFFSET(a1),-(sp)
  1053.     move.l    IO_LENGTH(a1),-(sp)
  1054.     PUTMSG    200,<'%s/RdWrt len %ld offset %ld data $%lx'>
  1055.     addq.l    #8,sp
  1056.     addq.l    #4,sp
  1057.     ENDC
  1058.  
  1059.     movem.l a2/a3,-(sp)
  1060.     move.l  a1,a2        ;Copy iob
  1061.     move.l  IO_UNIT(a2),a3      ;Get unit pointer
  1062.  
  1063. *      check operation for legality
  1064.     btst.b  #0,IO_DATA+3(a2)    ;check if user's pointer is ODD
  1065.     bne.s   IO_LenErr        ;bad...
  1066.     ;[D0=offset]
  1067.  
  1068.     move.l  IO_OFFSET(a2),d0
  1069.     move.l  d0,d1
  1070.     and.l   #SECTOR-1,d1    ;Bad sector boundary or alignment?
  1071.     bne.s   IO_LenErr        ;bad...
  1072.     ;[D0=offset]
  1073.  
  1074. *      check for IO within disc range
  1075.     ;[D0=offset]
  1076.     add.l   IO_LENGTH(a2),d0    ;Add length to offset
  1077.     bcs.s   IO_LenErr        ;overflow... (important test)
  1078.     cmp.l   #RAMSIZE,d0     ;Last byte is highest acceptable total
  1079.     bhi.s   IO_LenErr        ;bad... (unsigned compare)
  1080.     and.l   #SECTOR-1,d0    ;Even sector boundary?
  1081.     bne.s   IO_LenErr        ;bad...
  1082.  
  1083. *      We've gotten this far, it must be a valid request.
  1084.  
  1085.     IFD   INTRRUPT
  1086.      move.l   mdu_SigMask(a3),d0  ; Get signals to wait for
  1087.      LINKSYS  Wait,md_SysLib(a6)  ; Wait for interrupt before proceeding
  1088.     ENDC
  1089.  
  1090.  
  1091.     lea.l   mdu_RAM(a3),a0      ; Point to RAMDISK "sector" for I/O
  1092.     add.l   IO_OFFSET(a2),a0    ; Add offset to ram base
  1093.     move.l  IO_LENGTH(a2),d0
  1094.     move.l  d0,IO_ACTUAL(a2)    ; Indicate we've moved all bytes
  1095.     beq.s   RdWrt_end        ;---deal with zero length I/O
  1096.     move.l  IO_DATA(a2),a1      ; Point to data buffer
  1097. ;
  1098. ;A0=ramdisk index
  1099. ;A1=user buffer
  1100. ;D0=length
  1101. ;
  1102.     cmp.b   #CMD_READ,IO_COMMAND+1(a2)  ; Decide on direction
  1103.     BEQ.S   CopyTheBlock
  1104.     EXG     A0,A1        ; For Write and Format, swap source & dest
  1105. CopyTheBlock:
  1106.     LINKSYS CopyMemQuick,md_SysLib(a6)  ;A0=source A1=dest D0=size
  1107.     ;CopyMemQuick is very fast
  1108.  
  1109. RdWrt_end:
  1110.     move.l  a2,a1
  1111.     movem.l (sp)+,a2/a3
  1112.     bra     TermIO    ;END
  1113.  
  1114.  
  1115.  
  1116. IO_LenErr:
  1117.     PUTMSG  10,<'bad length'>
  1118.     move.b  #IOERR_BADLENGTH,IO_ERROR(a2)
  1119. IO_End:
  1120.     clr.l   IO_ACTUAL(a2)       ;Initially, no data moved
  1121.     bra.s   RdWrt_end
  1122.  
  1123.  
  1124.  
  1125. ;
  1126. ; the Stop command stop all future io requests from being
  1127. ; processed until a Start command is received.    The Stop
  1128. ; command is NOT stackable: e.g. no matter how many stops
  1129. ; have been issued, it only takes one Start to restart
  1130. ; processing.
  1131. ;
  1132. ;Stop is rather silly for a ramdisk
  1133. MyStop:
  1134.    PUTMSG   30,<'%s/MyStop: called'>
  1135.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1136.    bra     TermIO
  1137.  
  1138.  
  1139. Start:
  1140.     PUTMSG   30,<'%s/Start: called'>
  1141.     bsr.s  InternalStart
  1142.     bra   TermIO
  1143.  
  1144.        ;[A3=unit A6=device]
  1145. InternalStart:
  1146.     move.l  a1,-(sp)
  1147.     ;------ turn processing back on
  1148.     bclr   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1149.     ;------ kick the task to start it moving
  1150.     move.b  MP_SIGBIT(a3),d1
  1151.     CLEAR   d0
  1152.     bset    d1,d0            ;prepared signal mask
  1153.     move.l  MP_SIGTASK(a3),a1       ;:FIXED:marco-task to signal
  1154.     LINKSYS Signal,md_SysLib(a6)    ;:FIXED:marco-a6 not a3
  1155.     move.l  (sp)+,a1
  1156.     rts
  1157.  
  1158.  
  1159. ;
  1160. ; Flush pulls all I/O requests off the queue and sends them back.
  1161. ; We must be careful not to destroy work in progress, and also
  1162. ; that we do not let some io requests slip by.
  1163. ;
  1164. ; Some funny magic goes on with the STOPPED bit in here.  Stop is
  1165. ; defined as not being reentrant.  We therefore save the old state
  1166. ; of the bit and then restore it later.  This keeps us from
  1167. ; needing to DISABLE in flush.    It also fails miserably if someone
  1168. ; does a start in the middle of a flush. (A semaphore might help...)
  1169. ;
  1170.  
  1171. Flush:
  1172.    PUTMSG   30,<'%s/Flush: called'>
  1173.    movem.l   d2/a1/a6,-(sp)
  1174.  
  1175.    move.l   md_SysLib(a6),a6
  1176.  
  1177.    bset   #MDUB_STOPPED,UNIT_FLAGS(a3)
  1178.    sne     d2
  1179.  
  1180. Flush_Loop:
  1181.    move.l   a3,a0
  1182.    CALLSYS   GetMsg    ;Steal messages from task's port
  1183.  
  1184.    tst.l   d0
  1185.    beq.s   Flush_End
  1186.  
  1187.    move.l   d0,a1
  1188.    move.b   #IOERR_ABORTED,IO_ERROR(a1)
  1189.    CALLSYS   ReplyMsg
  1190.  
  1191.    bra.s   Flush_Loop
  1192.  
  1193. Flush_End:
  1194.    move.l   d2,d0
  1195.    movem.l   (sp)+,d2/a1/a6
  1196.  
  1197.    tst.b   d0
  1198.    beq.s   1$
  1199.  
  1200.    bsr     InternalStart
  1201. 1$:
  1202.    bra       TermIO
  1203.  
  1204.  
  1205. *****************************************************************************
  1206. ;
  1207. ; Here begins the task related routines
  1208. ;
  1209. ; A Task is provided so that queued requests may be processed at
  1210. ; a later time.  This is not very justifiable for a ram disk, but
  1211. ; is very useful for "real" hardware devices.  Take care with
  1212. ; your arbitration of shared hardware with all the multitasking
  1213. ; programs that might call you at once.
  1214. ;
  1215. ; Register Usage
  1216. ; ==============
  1217. ; a3 -- unit pointer
  1218. ; a6 -- syslib pointer
  1219. ; a5 -- device pointer
  1220. ; a4 -- task (NOT process) pointer
  1221. ; d7 -- wait mask
  1222. ;----------------------------------------------------------------------
  1223.  
  1224. ; some dos magic, useful for Processes (not us).  A process is started at
  1225. ; the first  executable address  after a segment list.    We hand craft a
  1226. ; segment list here.  See the the DOS technical reference if you really
  1227. ; need to know more about this.
  1228. ; The next instruction after the segment list is the first executable address
  1229.  
  1230.     cnop    0,4     ; long word align
  1231.     DC.L    16        ; segment length -- any number will do (this is 4
  1232.             ; bytes back from the segment pointer)
  1233. myproc_seglist:
  1234.     DC.L    0        ; pointer to next segment
  1235.  
  1236. Task_Begin:
  1237.     PUTMSG  35,<'%s/Task_Begin'>
  1238.     move.l  ABSEXECBASE,a6
  1239.  
  1240.     ;------ Grab the argument passed down from our parent
  1241.     move.l  4(sp),a3           ; Unit pointer
  1242.     move.l  mdu_Device(a3),a5  ; Point to device structure
  1243.  
  1244.     IFD   INTRRUPT
  1245.      ;------ Allocate a signal for "I/O Complete" interrupts
  1246.      moveq   #-1,d0        ; -1 is any signal at all
  1247.      CALLSYS   AllocSignal
  1248.      move.b   d0,mdu_SigBit(A3)   ; Save in unit structure
  1249.      moveq   #0,d7       ; Convert bit number signal mask
  1250.      bset   d0,d7
  1251.      move.l   d7,mdu_SigMask(A3)   ; Save in unit structure
  1252.      lea.l   mdu_is(a3),a1      ; Point to interrupt structure
  1253.      moveq   #INTB_PORTS,d0    ; Portia interrupt bit 3
  1254.      CALLSYS AddIntServer    ; Now install the server
  1255.      move.l   md_Base(a5),a0      ; Get board base address
  1256. *    bset.b   #INTENABLE,INTCTRL2(a0)   ; Enable interrupts
  1257.     ENDC
  1258.  
  1259.     ;------ Allocate a signal
  1260.     moveq   #-1,d0        ; -1 is any signal at all
  1261.     CALLSYS AllocSignal
  1262.     move.b  d0,MP_SIGBIT(a3)
  1263.     move.b  #PA_SIGNAL,MP_FLAGS(a3) ;Make message port "live"
  1264.     ;------ change the bit number into a mask, and save in d7
  1265.     moveq   #0,d7    ;Clear D7
  1266.     bset    d0,d7
  1267.  
  1268.     IFGE INFO_LEVEL-40
  1269.      move.l  $114(a6),-(sp)
  1270.      move.l  a5,-(sp)
  1271.      move.l  a3,-(sp)
  1272.      move.l  d0,-(sp)
  1273.      PUTMSG  40,<'%s/Signal=%ld, Unit=%lx Device=%lx Task=%lx'>
  1274.      add.l   #4*4,sp
  1275.     ENDC
  1276.  
  1277.     bra.s   Task_StartHere
  1278.  
  1279. ; OK, kids, we are done with initialization.  We now can start the main loop
  1280. ; of the driver.  It goes like this.  Because we had the port marked
  1281. ; PA_IGNORE for a while (in InitUnit) we jump to the getmsg code on entry.
  1282. ; (The first message will probably be posted BEFORE our task gets a chance
  1283. ; to run)
  1284. ;------     wait for a message
  1285. ;------     lock the device
  1286. ;------     get a message.  If no message, unlock device and loop
  1287. ;------     dispatch the message
  1288. ;------     loop back to get a message
  1289.  
  1290.     ;------ no more messages.  back ourselves out.
  1291. Task_Unlock:
  1292.     and.b   #$ff&(~(UNITF_ACTIVE!UNITF_INTASK)),UNIT_FLAGS(a3)
  1293.     ;------ main loop: wait for a new message
  1294.  
  1295. Task_MainLoop:
  1296.     PUTMSG   75,<'%s/++Sleep'>
  1297.     move.l  d7,d0
  1298.     CALLSYS Wait
  1299.     IFGE INFO_LEVEL-5
  1300.     bchg.b    #1,$bfe001  ;Blink the power LED
  1301.     ENDC
  1302. Task_StartHere:
  1303.     PUTMSG   75,<'%s/++Wakeup'>
  1304.     ;------ see if we are stopped
  1305.     btst    #MDUB_STOPPED,UNIT_FLAGS(a3)
  1306.     bne.s   Task_MainLoop    ; device is stopped, ignore messages
  1307.     ;------ lock the device
  1308.     bset    #UNITB_ACTIVE,UNIT_FLAGS(a3)
  1309.     bne     Task_MainLoop    ; device in use (immediate command?)
  1310.  
  1311.  
  1312.    ;------ get the next request
  1313. Task_NextMessage:
  1314.     move.l  a3,a0
  1315.     CALLSYS GetMsg
  1316.     PUTMSG  1,<'%s/GotMsg'>
  1317.     tst.l   d0
  1318.     beq     Task_Unlock ; no message?
  1319.  
  1320.     ;------ do this request
  1321.     move.l  d0,a1
  1322.     exg     a5,a6    ; put device ptr in right place
  1323.     bsr     PerformIO
  1324.     exg     a5,a6    ; get syslib back in a6
  1325.  
  1326.     bra.s   Task_NextMessage
  1327.  
  1328. *****************************************************************************
  1329. ;
  1330. ; Here is a dummy interrupt handler, with some crucial components commented
  1331. ; out.    If the IFD INTRRUPT is enabled, this code will cause the device to
  1332. ; wait for a level two interrupt before it will process each request
  1333. ; (pressing RETURN on the keyboard will do it).  This code is normally
  1334. ; disabled, and must fake or omit certain operations since there  isn't
  1335. ; really any hardware for this driver.    Similar code has been used
  1336. ; successfully in other, "REAL" device drivers.
  1337. ;
  1338.  
  1339.    IFD     INTRRUPT
  1340.  
  1341. ;   A1 should be pointing to the unit structure upon entry! (IS_DATA)
  1342. myintr:
  1343. *      move.l    md_Base(a0),a0      ; point to board base address
  1344. *      btst.b    #IAMPULLING,INTCTRL1(a0);See if I'm interrupting
  1345. *      beq.s   myexnm          ; if not set, exit, not mine
  1346. *      move.b    #0,INTACK(a0)      ; toggle controller's int2 bit
  1347.  
  1348. ;      ------ signal the task that an interrupt has occurred
  1349.  
  1350.     move.l    mdu_Device(a1),a0   ; Get device pointer
  1351.     move.l    mdu_SigMask(a1),d0
  1352.     lea.l    mdu_tcb(a1),a1
  1353.     move.l    md_SysLib(a0),a6   ; Get pointer to system
  1354.     CALLSYS Signal
  1355.  
  1356. ;      now clear the zero condition code so that
  1357. ;      the interrupt handler doesn't call the next
  1358. ;      interrupt server.
  1359. ;
  1360. *      moveq   #1,d0         clear zero flag
  1361. *      bra.s   myexit          now exit
  1362. ;
  1363. ;      this exit point sets the zero condition code
  1364. ;      so the interrupt handler will try the next server
  1365. ;      in the interrupt chain
  1366. ;
  1367. myexnm        moveq   #0,d0      set zero condition code
  1368. ;
  1369. myexit        rts
  1370.    ENDC
  1371.  
  1372.  
  1373. *****************************************************************************
  1374.  
  1375. mdu_Init:
  1376. ;   ------ Initialize the device
  1377.  
  1378.     INITBYTE    MP_FLAGS,PA_IGNORE  ;Unit starts with a message port
  1379.     INITBYTE    LN_TYPE,NT_MSGPORT  ;
  1380.     INITLONG    LN_NAME,myName        ;
  1381.     INITLONG    mdu_tcb+LN_NAME,myName
  1382.     INITBYTE    mdu_tcb+LN_TYPE,NT_TASK
  1383.     INITBYTE    mdu_tcb+LN_PRI,5
  1384.     IFD   INTRRUPT
  1385.      INITBYTE     mdu_is+LN_PRI,4      ; Int priority 4
  1386.      INITLONG     mdu_is+IS_CODE,myintr    ; Interrupt routine addr
  1387.      INITLONG     mdu_is+LN_NAME,myName
  1388.     ENDC
  1389.     DC.W   0
  1390.  
  1391.  IFNE  AUTOMOUNT
  1392. mdn_Init:
  1393. *   ;------ Initialize packet for MakeDosNode
  1394.  
  1395.     INITLONG    mdn_execName,myName    ; Address of driver name
  1396.     INITLONG    mdn_tableSize,12    ; # long words in AmigaDOS env.
  1397.     INITLONG    mdn_dName,$524d0000    ; Store 'RM' in name
  1398.     INITLONG    mdn_sizeBlock,SECTOR/4    ; # longwords in a block
  1399.     INITLONG    mdn_numHeads,1        ; RAM disk has only one "head"
  1400.     INITLONG    mdn_secsPerBlk,1    ; secs/logical block, must = "1"
  1401.     INITLONG    mdn_blkTrack,SECTORSPER ; secs/track (must be reasonable)
  1402.     INITLONG    mdn_resBlks,1        ; reserved blocks, MUST > 0!
  1403.     INITLONG    mdn_upperCyl,(RAMSIZE/BYTESPERTRACK)-1 ; upper cylinder
  1404.     INITLONG    mdn_numBuffers,1    ; # AmigaDOS buffers to start
  1405.     DC.W   0
  1406.  ENDC
  1407.  
  1408. ;----------------------------------------------------------------------
  1409. ; EndCode is a marker that shows the end of your code.    Make sure it does not
  1410. ; span hunks, and is not before the rom tag!  It is ok to put it right after
  1411. ; the rom tag -- that way you are always safe.    I put it here because it
  1412. ; happens to be the "right" thing to do, and I know that it is safe in this
  1413. ; case (this program has only a single code hunk).
  1414. ;----------------------------------------------------------------------
  1415. EndCode:    END
  1416.